package com.zzq.demo.sign;

import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * @author : zhaoziqian
 * @date : 2019/9/24 17:32
 */
public class RSASignUtil {

    private static final String KEY_ALGORITHM = "RSA";
    private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";

    /**
     * 加签数据
     *
     * @param data       待加签数据
     * @param privateKey 私钥
     * @return signedData 加签值（十六进制）
     */
    public static String sign(String data, String privateKey) throws Exception {
        try {
            byte[] signData = sign(data, hexStringToByte(privateKey));

            return byteToHexString(signData);

        } catch (Exception e) {
            throw new Exception("signature.sign.error : " + e.getMessage());
        }
    }

    /**
     * 加签数据
     *
     * @param data            待加签数据
     * @param privateKeyBytes 私钥
     * @return signedData
     */
    public static byte[] sign(String data, byte[] privateKeyBytes)
            throws Exception {

        try {
            PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(privateKeyBytes);
            KeyFactory keyf = KeyFactory.getInstance(KEY_ALGORITHM);
            PrivateKey key = keyf.generatePrivate(priPKCS8);

            // 进行签名服务
            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
            signature.initSign(key);
            signature.update(data.getBytes());
            byte[] signData = signature.sign();

            // 返回签名结果
            return signData;
        } catch (Exception e) {
            throw new Exception("signature.sign.error : " + e.getMessage());
        }
    }

    /**
     * 根据对签名数据使用签名者的公钥来解密后验证是否与原数据相同。从而确认用户签名正确
     *
     * @param data      被签名数据
     * @param signStr   使用该用户的私钥生成的已签名数据（十六进制）
     * @param publicKey 公钥（十六进制）
     * @return true或false，验证成功为true。
     * @throws Exception
     */
    public static boolean verify(String data, String signStr, String publicKey)
            throws Exception {
        try {
            return verify(data, hexStringToByte(signStr),
                    hexStringToByte(publicKey));
        } catch (Exception e) {
            throw new Exception("signature.verify.error : " + e.getMessage());
        }
    }

    /**
     * 根据对签名数据使用签名者的公钥来解密后验证是否与原数据相同。从而确认用户签名正确
     *
     * @param data           被签名数据
     * @param signStrBytes   使用该用户的私钥生成的已签名数据
     * @param publicKeyBytes 公钥
     * @return true或false，验证成功为true。
     * @throws Exception
     */
    public static boolean verify(String data, byte[] signStrBytes,
                                 byte[] publicKeyBytes) throws Exception {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            PublicKey pubKey = keyFactory
                    .generatePublic(new X509EncodedKeySpec(publicKeyBytes));

            // 进行验证签名服务
            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
            signature.initVerify(pubKey);
            signature.update(data.getBytes());
            return signature.verify(signStrBytes);
        } catch (Exception e) {
            throw new Exception("signature.verify.error : " + e.getMessage());
        }
    }

    /**
     * 二进制byte[]转十六进制string
     */
    public static String byteToHexString(byte[] bytes) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < bytes.length; i++) {
            String strHex = Integer.toHexString(bytes[i]);
            if (strHex.length() > 3) {
                sb.append(strHex.substring(6));
            } else {
                if (strHex.length() < 2) {
                    sb.append("0" + strHex);
                } else {
                    sb.append(strHex);
                }
            }
        }
        return sb.toString();
    }

    /**
     * 十六进制string转二进制byte[]
     */
    public static byte[] hexStringToByte(String s) throws Exception {
        byte[] baKeyword = new byte[s.length() / 2];
        for (int i = 0; i < baKeyword.length; i++) {
            try {
                baKeyword[i] = (byte) (0xff & Integer.parseInt(
                        s.substring(i * 2, i * 2 + 2), 16));
            } catch (Exception e) {
                System.out.println("十六进制转byte发生错误！！！");
                throw (e);
            }
        }
        return baKeyword;
    }

    public static void main(String[] args) throws Exception {
        try {

            // 生成 公私密钥
            // genKey();

            // 测试用
            String private_key = "30820276020100300D06092A864886F70D0101010500048202603082025C02010002818100B2FA501F77340FA79D4BB83C48E597FBC4CD1B7DF98A8AEBF89AD7977385BF1C252ECDA30AD3DA1133EFCB935E2331E85398A1DB33DA322C4C9E8FFDAA3244280B9A68421BEE6355510E5D0F8F1E9ED60FB4D3FEA4D978622E19B5903B04B445E07AFD5753E9AF08D36C2CDAA844BF01D089A1313AE3E05C1A438B7346342EC1020301000102818010801746EF5C735D06996680D84FA27569AD672D74061BCCA97CCD077F75032B7B1A178742864982708159095B126F9575AF8AAC84D946EAE29D73B4F52DF12E80712D88FE8DBB078B8900CB6F5619A6EA7EAA2AC177BCC300B27CFE5DDF2A401C4B1ABDAC66C58D0A62073E1E84533A4AD0B599C55217E57D193F4D55720B89024100DFE905D9B6B27BB7C488FF20D1AC2D4D38CB95ABCBFE195661B47B4CC6254AA8DB3F4A1E2329254F738FB84A2033BCB47FEA44101C0492800C9A29222E8CE64B024100CCA0C6BB9B447B2DC0B29AAC36A52935478C6CC928EA5C4D94675BF430156308C9709E6DD9B481C4395805B7DC72E50A5E4ED7D011E9ACF4ABABBD6347A487A3024100DFE3C668D309173242BFE5B3F054249C31AD4804979E85C15D433583282243CC2226DFBAD0D7A4AA14586223249DF713D94FEB2CDC8729E20637E3E77CFA808F02400324E948D2AEEFB1994765470623ED892B0B4654A4F34CEDC83CFA55BB3368ED26A33B5BF6BF664B0C9284ABF9902E62C56632BCB8B9BD7598F9AF9449F2863F02402DC81B3ADB91F58CF130DD62D8A2CC3E217DD365F445E6A516043E75A09CFD64513ADFF3FD70A7C29CBAF8682F88849011682E61243F0D5036AB707497E62B7F";
            String public_key = "30819F300D06092A864886F70D010101050003818D0030818902818100B2FA501F77340FA79D4BB83C48E597FBC4CD1B7DF98A8AEBF89AD7977385BF1C252ECDA30AD3DA1133EFCB935E2331E85398A1DB33DA322C4C9E8FFDAA3244280B9A68421BEE6355510E5D0F8F1E9ED60FB4D3FEA4D978622E19B5903B04B445E07AFD5753E9AF08D36C2CDAA844BF01D089A1313AE3E05C1A438B7346342EC10203010001";

            String source = "欢迎您光临,小木人印象http://www.xwood.net网站！！！";
            System.out.println("原文： ");
            System.out.println(source);

            // 加签
            String signStr = sign(source, private_key);

            System.out.println("加签: ");
            System.out.println(signStr);

            // 验签
            boolean flag = verify(source, signStr, public_key);
            System.out.println("验签结果 : ");
            System.out.println(flag);

        } catch (Exception e) {
            System.out.println(e.getMessage());
        }

    }

}
